
export function isObject(x: any) {
  const type = typeof x;
  return x !== null && (type === 'object' || type === 'function');
}

export function deepAssign<T, U>(to: T, from: U, merge: boolean = false): T {
  const hasOwnProperty = Object.prototype.hasOwnProperty;
  const toAny = to as any;
  const newSource = Object({});

  for (const key in from) {
    if (hasOwnProperty.call(from, key) && (merge || hasOwnProperty.call(to, key))) {
      const val = from[key];
      if (!isObject(val) || val instanceof Array) {
        newSource[key] = val;
      } else {
        let keyValue = toAny[key];
        if (keyValue !== undefined) {
          deepAssign(keyValue, val);
        } else {
          newSource[key] = val;
        }
      }
    }
  }

  Object.assign(to, newSource);
  return to;
}

export function deepMerge<T, U>(to: T, from: U): T {
  return deepAssign(to, from, true);
}

export function deepClone(source: any): any {
  return JSON.parse(JSON.stringify(source))
}
